using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Drawing;

namespace MyRobot.Nodes.Services
{
    public class RoboRealm : ProcessNode
    {
        #region Declarations
        public static RR_API.RR_API RoboRealmAPI;
        private int offsetX, offsetY;
        private int lastImageCount, rovioIR;
        private int halfWidth, halfHeight;
        private Dictionary<string, string> RRVariables = new Dictionary<string,string>();
        #endregion

        #region Properties
        private PropertyClass _Properties;

        public new class PropertyClass : ProcessNode.PropertyClass
        {
            public Point COG, COG_degrees, HighestPoint;
            public int CogArea, CogSize, ImageWidth, ImageHeight, BlobRadius, BlobArea;
            public int ImageCount;
            public double FPS;
            public int RovioLeftTick, RovioRightTick, RovioRearTick;
            public bool RovioIR;
            public bool Connected = false;
            public string SpeechEnabled = "NO";
        }

        public new PropertyClass Properties
        {
            get { return _Properties; }
            set { _Properties = value; }
        }

        #endregion

        #region Constructor
        public RoboRealm(string Name) : base(Name)
        {
            _Properties = new PropertyClass();

            this.Properties.UpdateInterval = 100;

            RoboRealmAPI = new RR_API.RR_API();
            if (!RoboRealmAPI.connect("localhost"))
            {
                MessageBox.Show("Ooops! RoboRealm is not running!");
            }
            else
            {
                this.Properties.Connected = true;
                SetVariable("SPEECH_ENABLED", "NO");
            }
        }
        #endregion

        public override void Process()
        {
            // Here we send all the latest RoboRealm variable values to the RoboRealm instance.
            lock (this)
            {
                foreach (string name in RRVariables.Keys)
                {
                    RoboRealmAPI.setVariable(name, RRVariables[name]);
                }
            }
        }

        public override void Run_Node()
        {
            lastImageCount = Convert.ToInt32(RoboRealmAPI.getVariable("IMAGE_COUNT"));

            while (true)
            {
                this.Properties.COG.X = Convert.ToInt32(RoboRealmAPI.getVariable("COG_X"));
                this.Properties.COG.Y = Convert.ToInt32(RoboRealmAPI.getVariable("COG_Y"));
                this.Properties.CogArea = Convert.ToInt32(RoboRealmAPI.getVariable("COG_AREA"));
                this.Properties.CogSize = Convert.ToInt32(RoboRealmAPI.getVariable("COG_BOX_SIZE"));
                this.Properties.FPS = Convert.ToDouble(RoboRealmAPI.getVariable("FPS"));
                this.Properties.ImageCount = Convert.ToInt32(RoboRealmAPI.getVariable("IMAGE_COUNT"));
                this.Properties.HighestPoint.X = Convert.ToInt16(RoboRealmAPI.getVariable("HIGHEST_MIDDLE_X"));
                this.Properties.HighestPoint.Y = (int)Convert.ToDouble(RoboRealmAPI.getVariable("HIGHEST_MIDDLE_Y"));
                this.Properties.ImageWidth = Convert.ToInt16(RoboRealmAPI.getVariable("IMAGE_WIDTH"));
                this.Properties.ImageHeight = Convert.ToInt16(RoboRealmAPI.getVariable("IMAGE_HEIGHT"));
                this.Properties.BlobRadius = Convert.ToInt16(RoboRealmAPI.getVariable("MAX_RADIUS"));
                this.Properties.BlobArea = Convert.ToInt32(RoboRealmAPI.getVariable("AREA"));
                this.Properties.RovioLeftTick = Convert.ToInt32(RoboRealmAPI.getVariable("ROVIO_LEFT_TICK"));
                this.Properties.RovioRightTick = Convert.ToInt32(RoboRealmAPI.getVariable("ROVIO_RIGHT_TICK"));
                this.Properties.RovioRearTick = Convert.ToInt32(RoboRealmAPI.getVariable("ROVIO_REAR_TICK"));
                rovioIR = Convert.ToInt16(RoboRealmAPI.getVariable("ROVIO_RADAR"));
                if (rovioIR == 1) MyRobot.myNodes.myRovio.Properties.Radar.Properties.Value = 1;
                else MyRobot.myNodes.myRovio.Properties.Radar.Properties.Value = 0;

                if (this.Properties.ImageWidth != 0 && this.Properties.ImageHeight != 0)
                {
                    halfWidth = this.Properties.ImageWidth / 2;
                    halfHeight = this.Properties.ImageHeight / 2;
                }
                else
                {
                    // Assume a 320x240 image as a fall back.
                    halfHeight = 120;
                    halfWidth = 160;
                }

                if (this.Properties.CogArea != 0 && halfWidth != 0 && halfHeight != 0)
                {
                    offsetX = this.Properties.COG.X - halfWidth;
                    offsetY = this.Properties.COG.Y - halfHeight;
                    this.Properties.COG_degrees.X = MyRobot.myNodes.myRovio.Properties.Camera.Properties.FOV_X * offsetX / (halfWidth * 2);
                    this.Properties.COG_degrees.Y = MyRobot.myNodes.myRovio.Properties.Camera.Properties.FOV_Y * offsetY / (halfHeight * 2);
                }
                else
                {
                    offsetX = 0;
                    offsetY = 0;
                    this.Properties.COG_degrees.X = 0;
                    this.Properties.COG_degrees.Y = 0;
                }

                Process();

                Thread.Sleep(this.Properties.UpdateInterval);
            }
        }

        public void Disconnect()
        {
            RoboRealmAPI.disconnect();
        }

        public void RovioDrive(MyRobot.RovioDriveCodes driveCode, int speed)
        {
            int RovioCode = (int)driveCode;
            SetVariable("ROVIO_DRIVE_CODE", RovioCode.ToString());
            SetVariable("ROVIO_SPEED", speed.ToString());
        }

        public void SetVariable(string Name, string Value)
        {
            // We need to lock this since Process() will throw an exception when modifying the list.
            lock (this)
            {
                if (!RRVariables.ContainsKey(Name)) RRVariables.Add(Name, null);
                RRVariables[Name] = Value;
            }
        }
    }
}
